package ssm.ann.controller;

import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ConcurrentModel;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.support.WebApplicationObjectSupport;
import org.springframework.web.method.annotation.ErrorsMethodArgumentResolver;
import org.springframework.web.method.annotation.ModelMethodProcessor;
import org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;

import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.InputStream;
import java.io.Writer;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * https://blog.csdn.net/f641385712/article/details/98989698
 * https://blog.csdn.net/f641385712/article/details/99980042
 * https://blog.csdn.net/f641385712/article/details/100016055
 */
@Controller
// @RestController
public class My_06_RequestMapping extends WebApplicationObjectSupport {

    /**
     * /pathVariable/abc
     *
     * @see UriComponentsBuilderMethodArgumentResolver
     */
    // @GetMapping("hw")
    @ResponseBody
    @RequestMapping({"pathVariable/{id}/{name}", "pathVariable"})
    public Object pathVariable(@PathVariable(name = "id", required = false) String xx,
                               @PathVariable(name = "name", required = false) String name,
                               @PathVariable Map<String, Object> kv) {
        // 继承自 ApplicationObjectSupport 就可以很方便的获取到下面这两个值
        System.out.println(super.getApplicationContext());
        System.out.println(super.obtainApplicationContext()); // @since 5.0
        // MessageSourceAccessor 参考：MessageSourceAware 它是对 MessageSource 的一个包装，处理国际化
        System.out.println(super.getMessageSourceAccessor());

        // 这里需要继承和 web 相关的：WebApplicationObjectSupport
        System.out.println(super.getWebApplicationContext());
        System.out.println(super.getServletContext());
        // Tomcat9_demowar\work\Catalina\localhost\demo_war_war
        System.out.println(super.getTempDir());
        System.out.println(name);
        System.out.println(kv);

        return Collections.singletonMap("hw", xx);
    }

    /**
     * 传数组
     * /pathVariableArray/fsx,fsx,fsx
     */
    @ResponseBody
    @GetMapping("/pathVariableArray/{objects}")
    public Object pathVariableArray(@PathVariable List<Object> objects) {
        System.out.println(objects);
        return objects;
    }

    // =======================================================================================================

    /**
     * /requestParam?id=abc
     */
    @ResponseBody
    @RequestMapping("requestParam")
    public Object requestParam(@RequestParam(name = "id", required = false) String xx) {
        return Collections.singletonMap("hw", xx);
    }

    /**
     * 传数组，但如果你这么写 String[] objects，即使不写注解，也能够正常完成正确封装。
     * 说明：Object[] objects 这么写的话不写注解是不行的
     * Spring MVC 的这么多 HandlerMethodArgumentResolver，它的解析是有顺序的：如果多个 HandlerMethodArgumentResolver 都可以解析某
     * 一种类型，以顺序在前面的先解析（后面的就不会再执行解析了），由于 RequestParamMethodArgumentResolver 同样可以对 Multipart 文件上传
     * 进行解析，并且默认顺序在 RequestPartMethodArgumentResolver 之前，所以如果不添加 @RequestPart 注解，Multipart 类型的参数会
     * 被 RequestParamMethodArgumentResolver 解析。
     * /requestParamArray/?objects=1,2,3
     * /requestParamArray/?objects=1&objects=2&objects=3
     */
    @ResponseBody
    @GetMapping("/requestParamArray")
    public Object requestParamArray(@RequestParam List<Object> objects) {
        System.out.println(objects);
        return objects;
    }

    /**
     * /requestParamMap?name=fsx&age=18&age=28
     */
    @ResponseBody
    @GetMapping("/requestParamMap")
    public Object requestParamMap(@RequestParam Map<String, Object> params) {
        System.out.println(params);
        return params;
    }

    // =======================================================================================================

    @ResponseBody
    @GetMapping("/requestHeader")
    public Object requestHeader(@RequestHeader("Accept-Encoding") String encoding,
                                @RequestHeader("Accept-Encoding") List<String> encodingList) {
        System.out.println(encoding);
        System.out.println(encodingList);
        return encoding;
    }

    /**
     * host-->localhost:8080
     * connection-->keep-alive
     * cache-control-->max-age=0
     * upgrade-insecure-requests-->1
     * user-agent-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36
     * sec-fetch-mode-->navigate
     * sec-fetch-user-->?1
     * sec-fetch-site-->none
     * accept-encoding-->gzip,deflate,br
     * accept-language-->zh-CN,zh;q=0.9
     * cookie-->JSESSIONID=123456789
     */
    @ResponseBody
    @GetMapping("/requestHeaderMap")
    public Object requestHeaderMap(@RequestHeader Map<String, Object> headers) {
        headers.forEach((k, v) -> System.out.println(k + "-->" + v));
        return headers;
    }

    // =======================================================================================================

    @GetMapping("/goto")
    public String gotoPage(HttpServletRequest request) {
        request.setAttribute("msg", "ok");
        request.getSession().setAttribute("code", "001");
        // return "redirect:/requestAttribute"; // 重定向
        return "forward:/requestAttribute"; // 转发
    }

    @ResponseBody
    @GetMapping("/requestAttribute")
    public Object requestAttribute(@RequestAttribute("Accept-Encoding") String encoding,
                                   @RequestAttribute("msg") String msg) {
        System.out.println(encoding);
        System.out.println(msg);
        return encoding;
    }

    // =======================================================================================================

    @ResponseBody
    @GetMapping("/sessionAttribute")
    public Object sessionAttribute(@SessionAttribute("Accept-Encoding") String encoding,
                                   @SessionAttribute("code") String code) {
        System.out.println(encoding);
        System.out.println(code);
        return encoding;
    }

    // =======================================================================================================

    @ResponseBody
    @GetMapping("/cookieValue")
    public Object cookieValue(@CookieValue("JSESSIONID") Cookie cookie,
                              @CookieValue("JSESSIONID") String cookieValue) {
        System.out.println(cookie);
        System.out.println(cookieValue);
        return cookieValue;
    }

    // =======================================================================================================

    /**
     * /matrixVariable/42;q=11/pets/21;s=23;q=22。
     */
    @ResponseBody
    @RequestMapping("/matrixVariable/{ByCriteria}")
    public Object matrixVariable(@MatrixVariable(pathVar = "ByCriteria") Map<String, List<String>> filterParams) {
        return filterParams;
    }

    // =======================================================================================================

    /**
     * 使用 PropertyPlaceholderConfigurer 导入会有容器隔离
     * 使用 @PropertySource 不会，因为子容器会把父环境给 merge 一份过来
     */
    @ResponseBody
    @GetMapping("/value")
    public Object value(@Value("#{T(Integer).parseInt('${test.myage:10}') + 10}") Integer myAge) {
        System.out.println(myAge);
        return myAge;
    }

    // =======================================================================================================

    /**
     * @see ServletRequestMethodArgumentResolver
     * @see ServletResponseMethodArgumentResolver
     * @see SessionStatusMethodArgumentResolver
     */
    @ResponseBody
    @SneakyThrows
    @GetMapping("/servletRequest")
    public Object servletRequest(SessionStatus sessionStatus, ServletResponse servletResponse, Writer writer,
                                 WebRequest webRequest, HttpSession httpSession, InputStream inputStream, Principal principal,
                                 HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        writer.write("servletRequest");
        // return "servletRequest";
        return null;
    }

    // =======================================================================================================

    /**
     * @see ModelMethodProcessor
     */
    @ResponseBody
    @GetMapping("/modelMethod")
    public Object modelMethod(Model model, RedirectAttributes redirectAttributes, ExtendedModelMap extendedModelMap,
                              RedirectAttributesModelMap redirectAttributesModelMap, ConcurrentModel concurrentModel) {
        return "modelMethod";
    }

    // =======================================================================================================

    /**
     * * @RequestBody/HttpEntity 它的参数（泛型）类型允许是 Map
     * * 方法上的和类上的 @ResponseBody 都可以被继承，但 @RequestBody 不可以
     * * @RequestBody它自带有 Bean Validation 校验能力（当然需要启用），HttpEntity 更加的轻量和方便
     *
     * @see AbstractMessageConverterMethodArgumentResolver
     * @see AbstractMessageConverterMethodProcessor
     */
    @ResponseBody
    @GetMapping("/requestBody")
    public Object requestBody(@RequestBody Map<String, Object> map, HttpEntity httpEntity, RequestEntity requestEntity) {
        return "requestBody";
    }

    // =======================================================================================================

    /**
     * 方法参数可以写 Errors 类型，来拿到数据校验结果
     *
     * @see ErrorsMethodArgumentResolver
     */
    @ResponseBody
    @GetMapping("/valid ")
    public Object valid(@Validated Object object1, @Valid Object object2, Error error) {
        return "valid";
    }

    // =======================================================================================================

    @Override
    protected void initApplicationContext() throws BeansException {
        // 这是父类提供给子类的（父类为空实现），子类可以自行实现，实现子类的逻辑
        // 比如子类 AbstractDetectingUrlHandlerMapping就复写了此方法去detectHandlers();
        super.initApplicationContext();
    }
}
